package com.gr.jron.webapp.action;

import com.gr.jron.Constants;
import com.gr.jron.model.Role;
import com.gr.jron.model.User;
import com.gr.jron.model.PagingQuery;
import com.gr.jron.service.UserExistsException;
import com.gr.jron.util.StringUtil;
import com.gr.jron.util.ServletTools;
import com.gr.jron.webapp.util.RequestUtil;
import com.opensymphony.xwork2.Preparable;
import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationTrustResolver;
import org.acegisecurity.AuthenticationTrustResolverImpl;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import org.apache.struts2.ServletActionContext;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import net.sf.json.JSONObject;

/**
 * Action for facilitating User Management feature.
 */
public class UserAction extends BaseAction implements Preparable {
    private static final long serialVersionUID = 6776558938712115191L;
    private List users;
    private User user;
    private String id;


    /**
     * Grab the entity from the database before populating with request parameters
     */
    public void prepare() {
        if (getRequest().getMethod().equalsIgnoreCase("post") && !"ExtUpdate".equals(getRequest().getParameter("from"))) {
            // prevent failures on new
            //&& !"ExtUpdate".equals(getRequest().getParameter("from")) is prevent .. ExtUpdate ..
            if (!"".equals(getRequest().getParameter("user.id"))
                    && !"JronExt".equals(getRequest().getParameter("from"))) {
                user = userManager.getUser(getRequest().getParameter("user.id"));
            }
        }
    }

    /**
     * Holder for users to display on list screen
     *
     * @return list of users
     */
    public List getUsers() {
        return users;
    }

    public void setId(String id) {
        this.id = id;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    /**
     * Delete the user passed in.
     *
     * @return success
     */
    public String delete() {
        userManager.removeUser(user.getId().toString());
        List<String> args = new ArrayList<String>();
        args.add(user.getFullName());
        saveMessage(getText("user.deleted", args));

        return SUCCESS;
    }

    /**
     * Grab the user from the database based on the "id" passed in.
     *
     * @return success if user found
     * @throws IOException can happen when sending a "forbidden" from response.sendError()
     */
    public String edit() throws IOException {
        HttpServletRequest request = getRequest();
        boolean editProfile = (request.getRequestURI().indexOf("editProfile") > -1);

        // if URL is "editProfile" - make sure it's the current user
        if (editProfile) {
            // reject if id passed in or "list" parameter passed in
            // someone that is trying this probably knows the AppFuse code
            // but it's a legitimate bug, so I'll fix it. ;-)
            if ((request.getParameter("id") != null) || (request.getParameter("from") != null)) {
                ServletActionContext.getResponse().sendError(HttpServletResponse.SC_FORBIDDEN);
                log.warn("User '" + request.getRemoteUser() + "' is trying to edit user '"
                        + request.getParameter("id") + "'");

                return null;
            }
        }

        // if a user's id is passed in
        if (id != null) {
            // lookup the user using that id
            user = userManager.getUser(id);
        } else if (editProfile) {
            user = userManager.getUserByUsername(request.getRemoteUser());
        } else {
            user = new User();
            user.addRole(new Role(Constants.USER_ROLE));
        }

        if (user.getUsername() != null) {
            user.setConfirmPassword(user.getPassword());

            // if user logged in with remember me, display a warning that they can't change passwords
            log.debug("checking for remember me login...");

            AuthenticationTrustResolver resolver = new AuthenticationTrustResolverImpl();
            SecurityContext ctx = SecurityContextHolder.getContext();

            if (ctx != null) {
                Authentication auth = ctx.getAuthentication();

                if (resolver.isRememberMe(auth)) {
                    getSession().setAttribute("cookieLogin", "true");
                    saveMessage(getText("userProfile.cookieLogin"));
                }
            }
        }

        return SUCCESS;
    }

    /**
     * Default: just returns "success"
     *
     * @return "success"
     */
    public String execute() {
        return SUCCESS;
    }

    /**
     * Sends users to "mainMenu" when !from.equals("list"). Sends everyone else to "cancel"
     *
     * @return "mainMenu" or "cancel"
     */
    public String cancel() {
        if (!"list".equals(from)) {
            return "mainMenu";
        }
        return "cancel";
    }

    /**
     * Save user
     *
     * @return success if everything worked, otherwise input
     * @throws IOException when setting "access denied" fails on response
     */
    public String save() throws IOException {
        Boolean encrypt = (Boolean) getConfiguration().get(Constants.ENCRYPT_PASSWORD);

        if ("true".equals(getRequest().getParameter("encryptPass")) && (encrypt != null && encrypt)) {
            String algorithm = (String) getConfiguration().get(Constants.ENC_ALGORITHM);

            if (algorithm == null) { // should only happen for test case
                log.debug("assuming testcase, setting algorithm to 'SHA'");
                algorithm = "SHA";
            }

            user.setPassword(StringUtil.encodePassword(user.getPassword(), algorithm));
        }

        Integer originalVersion = user.getVersion();

        boolean isNew = ("".equals(getRequest().getParameter("user.version")));
        // only attempt to change roles if user is admin
        // for other users, prepare() method will handle populating
        if (getRequest().isUserInRole(Constants.ADMIN_ROLE)) {
            user.getRoles().clear(); // APF-788: Removing roles from user doesn't work
            String[] userRoles = getRequest().getParameterValues("userRoles");

            for (int i = 0; userRoles != null && i < userRoles.length; i++) {
                String roleName = userRoles[i];
                user.addRole(roleManager.getRole(roleName));
            }
        }

        try {
            user = userManager.saveUser(user);
        } catch (AccessDeniedException ade) {
            // thrown by UserSecurityAdvice configured in aop:advisor userManagerSecurity
            log.warn(ade.getMessage());
            getResponse().sendError(HttpServletResponse.SC_FORBIDDEN);
            return null;
        } catch (UserExistsException e) {
            List<String> args = new ArrayList<String>();
            args.add(user.getUsername());
            args.add(user.getEmail());
            addActionError(getText("errors.existing.user", args));

            // reset the version # to what was passed in
            user.setVersion(originalVersion);
            // redisplay the unencrypted passwords
            user.setPassword(user.getConfirmPassword());
            return INPUT;
        }

        if (!"list".equals(from)) {
            // add success messages
            saveMessage(getText("user.saved"));
            return "mainMenu";
        } else {
            // add success messages
            List<String> args = new ArrayList<String>();
            args.add(user.getFullName());
            if (isNew) {
                saveMessage(getText("user.added", args));
                // Send an account information e-mail
                mailMessage.setSubject(getText("signup.email.subject"));
                sendUserMessage(user, getText("newuser.email.message", args),
                        RequestUtil.getAppURL(getRequest()));
                return SUCCESS;
            } else {
                saveMessage(getText("user.updated.byAdmin", args));
                return INPUT;
            }
        }
    }

    /**
     * Fetch all users from database and put into local "users" variable for retrieval in the UI.
     *
     * @return "success" if no exceptions thrown
     */
    public String list() {
        users = userManager.getUsers(new User());
        return SUCCESS;
    }

    /*******************************************************
     *                 Ext Grid Begin                       *
     *******************************************************/

    //Setting params callback => [ stcCallback1001 ] limit => [ 10 ]
    // start => [ 0 ] sort => [ username ] filterTxt => [ E-mail:  ] ajax => [ true ]
    // filterValue => [  ] _dc => [ 1190965290343 ] dir => [ ASC ]
    ///admin/jsonusers.html?ajax=true&start=0&limit=10&filterValue=&filterTxt=Choose%20Filter&sort=username
    // &dir=ASC&_dc=1190965290343&callback=stcCallback1001
    public void jsonUsers() throws Exception {
        PagingQuery pq = createPQ();
        JSONObject jsonObject = userManager.getJsonUsersPerPage(pq);
        log.debug(jsonObject);
        ServletTools.jsonResponse(jsonObject, getResponse(), callback,false);
    }

    public void jsonUser() throws Exception {
        JSONObject jsonObj = userManager.getJsonUser(id);
        log.debug(jsonObj);
        ServletTools.jsonResponse(jsonObj, getResponse(), callback,true);
    }

    public void jsonSaveUser() throws Exception {
    }

    public void jsonUpdateUser() throws Exception {
        if(id.equals("-1")) ServletTools.jsonMsgResponse(getText("signup.email.subject"),
                getResponse(), callback, true);
        if(id.equals("-2"))ServletTools.jsonMsgResponse(getText("userProfile.cookieLogin"),
                getResponse(), callback, false);
        log.debug("Over here! id: " + id);
    }

    private String start;
    private String limit;
    private String sort;
    private String filterValue;
    private String dir;

    private String callback;
    private String filterTxt;

    public void setStart(String start) {
        this.start = start;
    }

    public void setLimit(String limit) {
        this.limit = limit;
    }

    public void setSort(String sort) {
        this.sort = sort;
    }

    public void setCallback(String callback) {
        this.callback = callback;
    }

    public void setFilterTxt(String filterTxt) {
        this.filterTxt = filterTxt;
    }

    public void setFilterValue(String filterValue) {
        this.filterValue = filterValue;
    }

    public void setDir(String dir) {
        this.dir = dir;
    }

    public PagingQuery createPQ(){
        PagingQuery pq = new PagingQuery(Integer.parseInt(start), Integer.parseInt(limit),sort, filterValue, dir);
        pq.setFilterTxt(filterTxt);
        pq.setCallback(callback);
        return pq;
    }

    /*******************************************************
     *                 Ext Grid End                        *
     *******************************************************/

}
